This currently only supports executing the `src/main.rs` convention, no other.
Close #149
cargo-verify-project \
cargo-git-checkout \
cargo-test \
+ cargo-run
SRC = $(shell find src -name '*.rs' -not -path 'src/bin*')
"compile"
};
- let opts = CompileOptions {
+ let mut opts = CompileOptions {
update: options.update_remotes,
env: env,
shell: shell,
target: options.target.as_ref().map(|t| t.as_slice()),
};
- ops::compile(&root, opts).map(|_| None).map_err(|err| {
+ ops::compile(&root, &mut opts).map(|_| None).map_err(|err| {
CliError::from_boxed(err, 101)
})
}
--- /dev/null
+#![crate_name = "cargo-run"]
+#![feature(phase)]
+
+#[phase(plugin, link)]
+extern crate cargo;
+extern crate serialize;
+
+#[phase(plugin, link)]
+extern crate hammer;
+
+use std::os;
+use std::io::process::ExitStatus;
+
+use cargo::ops;
+use cargo::{execute_main_without_stdin};
+use cargo::core::{MultiShell};
+use cargo::util::{CliResult, CliError};
+use cargo::util::important_paths::find_project_manifest;
+
+#[deriving(PartialEq,Clone,Decodable)]
+struct Options {
+ manifest_path: Option<String>,
+ jobs: Option<uint>,
+ update: bool,
+ rest: Vec<String>,
+}
+
+hammer_config!(Options "Run the package's main executable", |c| {
+ c.short("jobs", 'j').short("update", 'u')
+})
+
+fn main() {
+ execute_main_without_stdin(execute);
+}
+
+fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
+ let root = match options.manifest_path {
+ Some(path) => Path::new(path),
+ None => try!(find_project_manifest(&os::getcwd(), "Cargo.toml")
+ .map_err(|_| {
+ CliError::new("Could not find Cargo.toml in this \
+ directory or any parent directory",
+ 102)
+ }))
+ };
+
+ let mut compile_opts = ops::CompileOptions {
+ update: options.update,
+ env: "compile",
+ shell: shell,
+ jobs: options.jobs,
+ target: None,
+ };
+
+ let err = try!(ops::run(&root, &mut compile_opts,
+ options.rest.as_slice()).map_err(|err| {
+ CliError::from_boxed(err, 101)
+ }));
+ match err {
+ None => Ok(None),
+ Some(err) => {
+ Err(match err.exit {
+ Some(ExitStatus(i)) => CliError::from_boxed(box err, i as uint),
+ _ => CliError::from_boxed(box err, 101),
+ })
+ }
+ }
+}
+
}))
};
- let compile_opts = ops::CompileOptions {
+ let mut compile_opts = ops::CompileOptions {
update: options.update,
env: "test",
shell: shell,
target: None,
};
- let test_executables = try!(ops::compile(&root, compile_opts).map_err(|err| {
+ let test_executables = try!(ops::compile(&root,
+ &mut compile_opts).map_err(|err| {
CliError::from_boxed(err, 101)
}));
println!("Commands:");
println!(" build # compile the current project");
println!(" test # run the tests");
- println!(" clean # remove the target directory\n");
-
+ println!(" clean # remove the target directory");
+ println!(" run # build and execute src/main.rs");
+ println!("");
+
let (_, options) = hammer::usage::<GlobalFlags>(false);
println!("Options (for all commands):\n\n{}", options);
pub target: Option<&'a str>,
}
-pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult<Vec<String>> {
- let CompileOptions { update, env, shell, jobs, target } = options;
+pub fn compile(manifest_path: &Path,
+ options: &mut CompileOptions) -> CargoResult<Vec<String>> {
+ let CompileOptions { update, env, ref mut shell, jobs, target } = *options;
let target = target.map(|s| s.to_string());
log!(4, "compile; manifest-path={}", manifest_path.display());
let source_ids = package.get_source_ids();
let (packages, resolve) = {
- let mut config = try!(Config::new(shell, update, jobs, target.clone()));
+ let mut config = try!(Config::new(*shell, update, jobs, target.clone()));
let mut registry =
try!(PackageRegistry::new(source_ids, override_ids, &mut config));
target.get_profile().get_env() == env
}).collect::<Vec<&Target>>();
- let mut config = try!(Config::new(shell, update, jobs, target));
+ let mut config = try!(Config::new(*shell, update, jobs, target));
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
&PackageSet::new(packages.as_slice()), &resolve, &mut config));
--- /dev/null
+use std::os;
+
+use ops;
+use util::{CargoResult, human, process, ProcessError};
+
+pub fn run(manifest_path: &Path,
+ options: &mut ops::CompileOptions,
+ args: &[String]) -> CargoResult<Option<ProcessError>> {
+ if !manifest_path.dir_path().join("src").join("main.rs").exists() {
+ return Err(human("`src/main.rs` must be present for `cargo run`"))
+ }
+
+ try!(ops::compile(manifest_path, options));
+ let exe = manifest_path.dir_path().join("target/main");
+ let exe = match exe.path_relative_from(&os::getcwd()) {
+ Some(path) => path,
+ None => exe,
+ };
+ let process = process(exe).args(args);
+
+ try!(options.shell.status("Running", process.to_string()));
+ Ok(process.exec().err())
+}
pub use self::cargo_compile::{compile, CompileOptions};
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
pub use self::cargo_rustc::compile_targets;
+pub use self::cargo_run::run;
mod cargo_clean;
mod cargo_compile;
mod cargo_read_manifest;
mod cargo_rustc;
+mod cargo_run;
--- /dev/null
+use std::path;
+
+use support::{project, execs};
+use support::{COMPILING, RUNNING};
+use hamcrest::{assert_that, existing_file};
+
+fn setup() {
+}
+
+test!(simple {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", r#"
+ fn main() { println!("hello"); }
+ "#);
+
+ assert_that(p.cargo_process("cargo-run"),
+ execs().with_status(0).with_stdout(format!("\
+{compiling} foo v0.0.1 (file:{dir})
+{running} `target{sep}main`
+hello
+",
+ compiling = COMPILING,
+ running = RUNNING,
+ dir = p.root().display(),
+ sep = path::SEP).as_slice()));
+ assert_that(&p.bin("main"), existing_file());
+})
+
+test!(simple_with_args {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {
+ assert_eq!(std::os::args().get(1).as_slice(), "hello");
+ assert_eq!(std::os::args().get(2).as_slice(), "world");
+ }
+ "#);
+
+ assert_that(p.cargo_process("cargo-run").arg("hello").arg("world"),
+ execs().with_status(0));
+})
+
+test!(exit_code {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", r#"
+ fn main() { std::os::set_exit_status(2); }
+ "#);
+
+ assert_that(p.cargo_process("cargo-run"),
+ execs().with_status(2));
+})
+
+test!(no_main_file {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "");
+
+ assert_that(p.cargo_process("cargo-run"),
+ execs().with_status(101)
+ .with_stderr("`src/main.rs` must be present for \
+ `cargo run`\n"));
+})
mod test_cargo_test;
mod test_shell;
mod test_cargo_cross_compile;
+mod test_cargo_run;